mod json;

use std::fmt::Debug;

use regex::Match;

pub type Result<R, E = String> = std::result::Result<(R, String), E>;

pub trait Parser {
    type Output: Debug;

    fn run(&self, input: impl AsRef<str>) -> Result<Self::Output>;
}

#[derive(Debug, Clone)]
pub struct AndThen<P1, P2>(P1, P2);

impl<P1: Parser, P2: Parser> Parser for AndThen<P1, P2> {
    type Output = (P1::Output, P2::Output);

    fn run(&self, input: impl AsRef<str>) -> Result<Self::Output, String> {
        let Self(p1, p2) = self;
        p1.run(input)
            .and_then(|(r1, rm1)| p2.run(rm1).map(|(r2, rm2)| ((r1, r2), rm2)))
    }
}

pub trait ParserAndThen: Sized {
    fn and_then<P2>(self, other: P2) -> AndThen<Self, P2>;
}

impl<T: Parser> ParserAndThen for T {
    fn and_then<T2>(self, other: T2) -> AndThen<Self, T2> {
        AndThen(self, other)
    }
}

#[derive(Debug, Clone)]
pub struct Map<T, F>(T, F);

impl<T, F, S> Parser for Map<T, F>
where
    T: Parser,
    F: Fn(T::Output) -> S,
    S: Debug,
{
    type Output = S;

    fn run(&self, input: impl AsRef<str>) -> Result<Self::Output> {
        self.0.run(input).map(|(t, r)| (self.1(t), r))
    }
}

pub trait ParserMap: Sized {
    fn map<M>(self, map: M) -> Map<Self, M>;
}

impl<T: Parser> ParserMap for T {
    fn map<M>(self, map: M) -> Map<Self, M> {
        Map(self, map)
    }
}

#[derive(Debug, Clone)]
pub struct Or<P1, P2>(P1, P2);

impl<P1, P2, O> Parser for Or<P1, P2>
where
    O: Debug,
    P1: Parser<Output = O>,
    P2: Parser<Output = O>,
{
    type Output = O;

    fn run(&self, input: impl AsRef<str>) -> Result<Self::Output> {
        let input = input.as_ref();
        match self.0.run(input) {
            Ok(r) => Ok(r),
            Err(err1) => match self.1.run(input) {
                Ok(r) => Ok(r),
                Err(err2) => Err(format!("unable to match {} and {}", err1, err2)),
            },
        }
    }
}

pub trait ParserOr: Sized {
    fn or<M>(self, _else: M) -> Or<Self, M>;
}

impl<T: Parser> ParserOr for T {
    fn or<M>(self, map: M) -> Or<Self, M> {
        Or(self, map)
    }
}

#[derive(Debug, Clone)]
pub struct Tag(String);

impl Parser for Tag {
    type Output = String;

    fn run(&self, input: impl AsRef<str>) -> Result<Self::Output, String> {
        let input = input.as_ref();
        if input.starts_with(&self.0) {
            let matched = self.0.clone();
            let remaining = input.strip_prefix(&self.0).unwrap().into();
            Ok((matched, remaining))
        } else {
            Err(format!("{} Not Found", self.0))
        }
    }
}

pub fn tag(s: impl AsRef<str>) -> Tag {
    Tag(s.as_ref().to_string())
}

#[derive(Debug, Clone)]
pub struct Re(regex::Regex);

impl Parser for Re {
    type Output = String;

    fn run(&self, input: impl AsRef<str>) -> Result<Self::Output> {
        let input = input.as_ref();
        match self.0.find(input) {
            Some(mat) => {
                if mat.start() == 0 {
                    let remaining = input.substr(mat.end());
                    Ok((mat.as_str().into(), remaining))
                } else {
                    Err(format!("re not found"))
                }
            }
            None => Err(format!("re not found")),
        }
    }
}

pub fn re(re: regex::Regex) -> Re {
    Re(re)
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn test_parse_abc() {
        let string = "abc";
        let parser = tag("ab");

        let r = parser.run(string).unwrap();

        assert_eq!(r, ("ab".into(), "c".into()));
    }

    #[test]
    fn test_andthen() {
        let p1 = tag("a");
        let p2 = tag("b");
        let parser = AndThen(p1, p2);
        let r = parser.run("abc").unwrap();

        assert_eq!(r, (("a".into(), "b".into()), "c".into()));
    }

    #[test]
    fn test_andthen_trait() {
        let p1 = tag("a");
        let p2 = tag("b");
        let parser = p1.and_then(p2);
        let r = parser.run("abc").unwrap();

        assert_eq!(r, (("a".into(), "b".into()), "c".into()));
    }

    #[test]
    fn test_or() {
        let p1 = tag("a");
        let p2 = tag("b");
        let parser = p2.or(p1);
        let r = parser.run("abc").unwrap();
        assert_eq!(r, ("a".into(), "bc".into()));
    }

    #[test]
    fn test_map() {
        let p = tag("123").map(|s: String| s.parse::<i32>().unwrap());
        let r = p.run("123000").unwrap();
        assert_eq!(r.0, 123);
    }
}
